﻿using AutoMoq;
using FluentAssertions;
using MoqaLate.CodeModel;
using MoqaLate.ExtensionMethods;
using MoqaLate.MockClassBuilding;
using NUnit.Framework;

namespace MoqaLate.Tests.Unit
{
    [TestFixture]
    public class ClassTextBuilderTests
    {
        private IClassTextBuilder _sut;


        [SetUp]
        public void Setup()
        {
            var mocker = new AutoMoqer();

            _sut = mocker.Resolve<ClassTextBuilder>();
        }


        [Test]
        public void ShouldCreateGenericInterfaceTypes()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               InterfaceGenericTypes = "<T,K>",
                               OriginalInterfaceName = "IAwesomeClass",
                           };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass<T,K> : IAwesomeClass<T,K>
    { 

    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }


        [Test]
        public void ShouldCreateEvents()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               OriginalInterfaceName = "IAwesomeClass",
                               Events =
                                   {
                                       new Event {Type = "EventHandler", Name = "E1"},
                                       new Event {Type = "Action", Name = "E2"},
                                       new Event {Type = "Action<int>", Name = "E3"},
                                       new Event {Type = "Action<int, string>", Name = "E4"},
                                       new Event {Type = " EventHandler<AssemblyLoadEventArgs>", Name = "E5"}
                                   }
                           };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    { 
        public virtual event EventHandler E1;
        public virtual event Action E2;
        public virtual event Action<int> E3;
        public virtual event Action<int, string> E4;
        public virtual event EventHandler<AssemblyLoadEventArgs> E5;       
    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }

        [Test]
        public void ShouldCreateMethods1()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               OriginalInterfaceName = "IAwesomeClass",
                               Methods =
                                   {
                                       new Method
                                           {
                                               ReturnType = "string",
                                               Name = "DoStuff1",
                                               Parameters = new MethodParameterList{ new MethodParameter{Type = "int", Name = "p1"},
                                                                                     new MethodParameter{Type = "List<int>", Name="p2"}}
                                           }                                      
                                   }
                           };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    {

        // -------------- DoStuff1 ------------ 

        private string _doStuff1ReturnValue;
        private int _doStuff1NumberOfTimesCalled;        

        public int DoStuff1Parameter_p1_LastCalledWith;
        public List<int> DoStuff1Parameter_p2_LastCalledWith;
        
        public virtual void DoStuff1SetReturnValue(string value)
        {
            _doStuff1ReturnValue = value;
        }

        public virtual bool DoStuff1WasCalled()
        {
            return _doStuff1NumberOfTimesCalled > 0;
        }

        public virtual bool DoStuff1WasCalled(int times)
        {
            return _doStuff1NumberOfTimesCalled == times;
        }

        public virtual int DoStuff1TimesCalled()
        {
            return _doStuff1NumberOfTimesCalled;
        }


        public virtual bool DoStuff1WasCalledWith(int p1, List<int> p2)
        {
            return (
                    p1.Equals(DoStuff1Parameter_p1_LastCalledWith) &&
                    p2.Equals(DoStuff1Parameter_p2_LastCalledWith)
                    );
        }

        public string DoStuff1(int p1, List<int> p2)
        {
            _doStuff1NumberOfTimesCalled++;            

            DoStuff1Parameter_p1_LastCalledWith = p1;
            DoStuff1Parameter_p2_LastCalledWith = p2;            

            return _doStuff1ReturnValue;
        }
   }
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }













   
        [Test]
        public void ShouldCreateMethods2()
        {
            var spec = new ClassSpecification
            {
                ClassName = "AwesomeClass",
                OriginalInterfaceName = "IAwesomeClass",
                Methods =
                                   {                                     
                                       new Method {ReturnType = "Func<bool, int>", Name = "DoStuff2"}
                                   }
            };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    {     
        // -------------- DoStuff2 ------------ 

        private Func<bool, int> _doStuff2ReturnValue;
        private int _doStuff2NumberOfTimesCalled;
               
        
        public virtual void DoStuff2SetReturnValue(Func<bool, int> value)
        {
            _doStuff2ReturnValue = value;
        }     

        public virtual bool DoStuff2WasCalled()
        {
            return _doStuff2NumberOfTimesCalled > 0;
        }


        public virtual bool DoStuff2WasCalled(int times)
        {
            return _doStuff2NumberOfTimesCalled == times;
        }

        public virtual int DoStuff2TimesCalled()
        {
            return _doStuff2NumberOfTimesCalled;
        }



        public Func<bool, int> DoStuff2()
        {
            _doStuff2NumberOfTimesCalled++;            

            return _doStuff2ReturnValue;
        }



    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }











        [Test]
        public void ShouldCreateMethods3()
        {
            var spec = new ClassSpecification
            {
                ClassName = "AwesomeClass",
                OriginalInterfaceName = "IAwesomeClass",
                Methods =
                                   {
                                       new Method {ReturnType = "void", Name = "DoStuff3"}
                                      
                                   }
            };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    {

        // -------------- DoStuff3 ------------ 

        private int _doStuff3NumberOfTimesCalled;
        
        public virtual bool DoStuff3WasCalled()
        {
            return _doStuff3NumberOfTimesCalled > 0;
        }

        public virtual bool DoStuff3WasCalled(int times)
        {
            return _doStuff3NumberOfTimesCalled == times;
        }     

        public virtual int DoStuff3TimesCalled()
        {
            return _doStuff3NumberOfTimesCalled;
        }
   
        
        public void DoStuff3()
        {
            _doStuff3NumberOfTimesCalled++;                        
        }


      

    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }












        [Test]
        public void ShouldCreateMethods4()
        {
            var spec = new ClassSpecification
            {
                ClassName = "AwesomeClass",
                OriginalInterfaceName = "IAwesomeClass",
                Methods =
                                   {
                                       new Method
                                           {
                                               ReturnType = "void",
                                               Name = "LetsDoIt",



                                               Parameters = new MethodParameterList{ new MethodParameter{Type = "Func<bool, int>", Name = "p1"},
                                                                                     new MethodParameter{Type = "string", Name="p2"}}

                                           }
                                   }
            };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    {

    
        // -------------- LetsDoIt ------------ 

        private int _letsDoItNumberOfTimesCalled;        

        public Func<bool, int> LetsDoItParameter_p1_LastCalledWith;
        public string LetsDoItParameter_p2_LastCalledWith;


        public virtual bool LetsDoItWasCalled()
        {
            return _letsDoItNumberOfTimesCalled > 0;
        }

        public virtual bool LetsDoItWasCalled(int times)
        {
            return _letsDoItNumberOfTimesCalled == times;
        }


       public virtual int LetsDoItTimesCalled()
        {
            return _letsDoItNumberOfTimesCalled;
        }

        public virtual bool LetsDoItWasCalledWith(Func<bool, int> p1, string p2)
        {
            return (
                    p1.Equals(LetsDoItParameter_p1_LastCalledWith) &&
                    p2.Equals(LetsDoItParameter_p2_LastCalledWith)
                    );
        }

        
        public void LetsDoIt(Func<bool, int> p1, string p2)
        {
            _letsDoItNumberOfTimesCalled++;
            
            LetsDoItParameter_p1_LastCalledWith = p1;
            LetsDoItParameter_p2_LastCalledWith = p2;
        }

    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }




















        [Test]
        public void ShouldCreateGenericMethods()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               InterfaceGenericTypes = "<T,K>",
                               OriginalInterfaceName = "IAwesomeClass",
                               Methods =
                                   {
                                       new Method{Name="M1", ReturnType="T", Parameters = new MethodParameterList{new MethodParameter{Type="K", Name="input"}}}
                                   }
                           };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated
{
public partial class AwesomeClass<T,K> : IAwesomeClass<T,K>
{


// -------------- M1 ------------ 

private T _m1ReturnValue;

        private int _m1NumberOfTimesCalled;

        public K M1Parameter_input_LastCalledWith;
        
        public virtual void M1SetReturnValue(T value)
        {
            _m1ReturnValue = value;
        }    


        public virtual bool M1WasCalled()
{
   return _m1NumberOfTimesCalled > 0;
}


public virtual bool M1WasCalled(int times)
{
   return _m1NumberOfTimesCalled == times;
}


public virtual int M1TimesCalled()
{
   return _m1NumberOfTimesCalled;
}


public virtual bool M1WasCalledWith(K input){
return (
input.Equals(M1Parameter_input_LastCalledWith) );
}
 

             public T M1(K input)
        {
            _m1NumberOfTimesCalled++;            

            M1Parameter_input_LastCalledWith = input;

            return _m1ReturnValue;
        }}
}

";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }



        [Test]
        public void ShouldCreateNamespacedClass()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               OriginalInterfaceName = "IAwesomeClass"
                           };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated { public partial class AwesomeClass : IAwesomeClass {} }";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }


        [Test]
        public void ShouldCreateNamespacesWithUsings()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               OriginalInterfaceName = "IAwesomeClass",
                               OriginalInterfaceNamespace = "My.Awesome.Namespace",
                               Usings = {"FluentAssertions", "MoqaLate.Extensions", "NUnit.Framework"}
                           };

            const string expectedClassText =
                @"
using FluentAssertions;
using MoqaLate.Extensions;
using NUnit.Framework;
using My.Awesome.Namespace;

namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    {
       
    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }



        [Test]
        public void ShouldWorkOkWhenOverloadedMethodsInInterfaceWithSameMethodNameButDiffReturnAndOrMethodSignature()
        {
            Assert.Inconclusive();
        }

        [Test]
        public void ShouldCreateProperties()
        {
            var spec = new ClassSpecification
                           {
                               ClassName = "AwesomeClass",
                               OriginalInterfaceName = "IAwesomeClass",
                               Properties =
                                   {
                                       new Property
                                           {Accessor = PropertyAccessor.GetAndSet, Name = "GandS", Type = "string"},
                                       new Property {Accessor = PropertyAccessor.GetOny, Name = "G", Type = "List<int>"},
                                       new Property {Accessor = PropertyAccessor.SetOny, Name = "S", Type = "float"}
                                   }
                           };

            const string expectedClassText =
                @"namespace MoqaLate.Autogenerated 
{ 
    public partial class AwesomeClass : IAwesomeClass
    {
        // ------------ Property GandS

        private string _GandS;

        public virtual string GandS 
        { 
            get
            {
                return _GandS;
            }
            set
            {
                _GandS = value;
            }
        }

        public virtual void __SetGandS(string val)
        {
            _GandS = val;
        }


        // ------------ Property G

        private List<int> _G;

        public virtual List<int> G
        { 
            get
            {
                return _G;
            }
        }

        public virtual void __SetG(List<int> val)
        {
            _G = val;
        }


        // ------------ Property S

        private float _S;

        public virtual float S
        { 
            set
            {
                _S = value;
            }
        }

        public virtual void __SetS(float val)
        {
            _S = val;
        }

    } 
}";

            var actualClassText = _sut.Create(spec);

            actualClassText.CanonicalString().Should().Be(expectedClassText.CanonicalString());
        }
    }
}